/**
 * jQuery Twi Form Validate plugin
 * @Author：demojone
 * @Version：1.0.2
 * @CreateDate：2015-03-01
 * @Example: Visit http://blog.163.com/twi_fly  
 * @Copyright ©2010-2015 Tyingsoft 踏影软件开发工作室 之 Twilight/Twifly开源系列. 转载请保留本行注明.   
 * @License：Twi Framework of Tyingsoft is licensed under the MIT license.  
 * @Description：基于jquery+Bootstrap的表单验证控件
 * @Require:  
 *
 * ------------------------------------------------------------------
 * 编号    版本号      作者              修改日期        修改内容
 * ------------------------------------------------------------------
 *  1      1.0.0      demojone         2015-03-01      twi表单验证第一版本修改验证样式。（注意）暂时处理不了的一个问题，验证中提示对错符号必须嵌套在input-group节点内！，因此暂时在代码中添加 <div class="input-group"></div>.在此节点内添加控件。随后可能会选择一个更好的解决方案
 *  2      1.0.1      demojone         2015-03-04      添加功能：表单验证提示时，1.点击提示框，所有提示隐藏；获取某个控件的焦点，该控件提示框消失；（上下布局的input-group组合会出现一些样式上的问题；请开发人员注意，随后会更新这个bug）
 *  3      1.0.2      demojone         2015-03-09      twi表单验证修改样式，修改提示框过高遮挡其他控件的问题
 * ------------------------------------------------------------------
 */


!function ($) {
    /**
     * Twi Form表单验证初始化
     */
    $.fn.twiFormValidate = function (callback, options) {

        if (!this.length) {
            if (options && options.debug && window.console) {
                console.warn("初始化twiFormValidate验证失败...");
            }
            return;
        }

        return this.each(function (a, b) {
            globalOptions = $.extend({}, $.fn.twiFormValidate.defaults, options);
            globalOptions.callback = callback;

            // Add novalidate tag if HTML5.
            $(this).attr("novalidate", "novalidate");
            fform_style = isformstyle(this);
            validationForm(this);
        });
    };

    /**
     * 表达验证，返回是否验证成功
     * @options：配置参数
     * @Return： true or false
     */
    $.fn.twiIsValidate = function (options) {
        if (formState) { // 重复提交则返回
            return false;
        }
        formState = true;
        var validationError = false;
        //取出验证的
        $('input, textarea', this).each(function () {
            var el = $(this),
                controlGroup = el.parents('.form-group'),
                //twiforvalidate="required chinese"  //支持多个，以空格隔开。
                valid = (el.attr('twiforvalidate') == undefined) ? null : el.attr('twiforvalidate').split(' ');

            if (!controlGroup.hasClass('has-success') && valid != null && valid.length > 0) {
                if (!validateField(this, valid)) {
                    if (wFocus == false) {
                        scrollTo(0, el[0].offsetTop - 50);
                        wFocus = true;
                    }
                    validationError = true;
                }
            }
        });

        wFocus = false;
        formState = false;
        return !validationError;
    };

    $.fn.twiFormValidate.defaults = {
        VERSION: '1.0.0',
        validPopover: "right", //提示框箭头指向  right、top、left
        validPopoverArrow: { top: 100, bottom: 0, left: 50, right: 50 },
        validRules: [
            { name: 'required', validate: function (value) { return ($.trim(value) == ''); }, defaultMsg: '请输入内容。' },
            { name: 'number', validate: function (value) { return (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)); }, defaultMsg: '请输入数字。' },
            { name: 'mail', validate: function (value) { return (!/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value)); }, defaultMsg: '请输入邮箱地址。' },
            { name: 'char', validate: function (value) { return (!/^[a-z\_\-A-Z]*$/.test(value)); }, defaultMsg: '请输入英文字符。' },
            { name: 'chinese', validate: function (value) { return (/[^\u4e00-\u9fa5]/gi.test(value)); }, defaultMsg: '请输入汉字。' },
            { name: 'url', validate: function (value) { return (!/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value)); }, defaultMsg: '请输入网址' },
            { name: 'date', validate: function (value) { return (/Invalid|NaN/.test(new Date(value).toString())); }, defaultMsg: "日期格式XXXX-XX-XX。" }
        ],
        reqmark: true,
        callback: null //function(obj,params){};           
    };

    var formState = false,
        fieldState = false,
        wFocus = false,
        fform_style = 0,    //0=表示基本表单 1=表示内联表单 2=水平排列的表单
        globalOptions = {};

    function isformstyle(form) {
        if ($(form).hasClass('form-inline')) {
            return 1;
        }
        else if ($(form).hasClass('form-horizontal')) {
            return 2;
        }
        else {
            return 0;
        };
    };

    /**
     * 验证字段
     */
    var validateField = function (field, valid) {
        var el = $(field), error = false, errorMsg = '';
        var minlength = (el.attr('minlength') ? el.attr('minlength') : null);
        var range = (el.attr('range') ? el.attr('range') : null); //
        var msg;
        for (i = 0; i < valid.length; i++) {
            var x = true,
                flag = valid[i];
            msg = (el.attr('twi' + flag + 'msg') == undefined) ? null : el.attr('twi' + flag + 'msg');

            if (flag.substr(0, 1) == '!') {
                x = false;
                flag = flag.substr(1, flag.length - 1);
            }

            var rules = globalOptions.validRules;
            for (j = 0; j < rules.length; j++) {
                var rule = rules[j];
                if (flag == rule.name) {
                    var value;
                    if (el.attr('type') == 'checkbox') {
                        value = el.is(":checked") ? 'true' : '';
                    }
                    else {
                        value = el.val();
                    };
                    if (rule.validate.call(field, value) == x) {
                        error = true;
                        if (el.attr('type').toLowerCase() == 'file') {
                            errorMsg = (msg == null) ? '请选择文件。' : msg;
                        }
                        else {
                            errorMsg = (msg == null) ? rule.defaultMsg : msg;
                        }
                        break;
                    }
                }
            }
            if (error) { break; }
        }

        //验证长度
        if (minlength && !error) {
            error = el.val().length < minlength;
            if (error && (msg == null || errorMsg == '')) {
                errorMsg = '输入长度大于等于' + minlength;
            }
        };

        //值区间
        if ($.inArray('number', valid) >= 0 && range && !error) {
            var values = range.split("~");

            if (values.length == 2) {
                error = parseFloat(el.val()) < parseFloat(values[0]) || parseFloat(el.val()) > parseFloat(values[1]);
                if (error && (msg == null || errorMsg == '')) {
                    errorMsg = '输入值在［' + values[0] + '~' + values[1] + ']之间。';
                }
            }
            else {
                var values = range.split(",");
                if (values.length > 0) {
                    //error =  values.indexOf(el.val())<0;
                    error = $.inArray(el.val(), values) < 0;
                    if (error && (msg == null || errorMsg == '')) {
                        errorMsg = '输入值为' + range + '的其中一个。';
                    }
                }
            }
        };

        //外部验证回调方法
        if (!error && globalOptions.callback) {
            var params = {
                msg: '',
                err: error
            };
            var b = $.ajaxSettings.async;
            $.ajaxSetup({ async: false });
            globalOptions.callback(field, params);
            error = params.err;
            if (error && (msg == null || errorMsg == '')) {
                errorMsg = params.msg;
            }
            else if (params.msg != '') {
                errorMsg = params.msg;
            }
            $.ajaxSetup({ async: b });
        };


        var controlGroup = el.parents('.form-group');
        controlGroup.removeClass('has-error has-success');
        controlGroup.addClass(error == false ? 'has-success' : 'has-error');
        var form = el.parents("form");
        if (form) {
            var fstyle = isformstyle(form);
            controlGroup.find("#valierr").remove();
            controlGroup.find(".twivalierr").remove();
            controlGroup.find(".popover").remove();
            var popover = globalOptions.validPopover;
            var arrow = "0";
            var textFontSize = 20;// parseFloat(thisEle, 10);
            var contentWidth = errorMsg.length * textFontSize;
            var width = controlGroup.width();
            var height = -(controlGroup.find('.form-control').get(0).offsetHeight - (el.parents('.input-group').height() == null ? controlGroup.find('.form-control').height() : el.parents('.input-group').height()));
            var heightFeedback = height;

            if ($(window).width() < (width + contentWidth)) {
                popover = "top";
                width = 0;
                arrow = globalOptions.validPopoverArrow.top;
                height = -controlGroup.find('.form-control').get(0).offsetHeight;
                heightFeedback = 0;
            } else {
                popover = "right";

            }
            for (var item in globalOptions.validPopoverArrow) {
                if (item == popover) {
                    var jValue = globalOptions.validPopoverArrow[item];
                    arrow = jValue;
                }
            }

            if (fstyle == 0) {

                if (el.parents('.input-group').length != 0) {
                    if (errorMsg == "") {
                        el.after('<span class="glyphicon glyphicon-ok form-control-feedback twivalierr" style="top: ' + height + 'px;"  aria-hidden="true"></span>');
                    }
                    else {
                        el.after('<span title=' + errorMsg + ' style="top: ' + heightFeedback + 'px;" class="glyphicon glyphicon-remove form-control-feedback twivalierr" aria-hidden="true"></span>');
                        controlGroup.find(".twivalierr").after('<div class="popover fade ' + popover + ' in" role="tooltip"  style="top: ' + height + 'px; left: ' + width + 'px;display: block;"><div class="arrow" style="top: ' + arrow + '%;"></div><div class="popover-content" style="width:' + contentWidth + 'px">' + errorMsg + '</div></div>');
                    }

                }
                else {

                    controlGroup.after('<span title=' + errorMsg + ' style="top: ' + heightFeedback + 'px;" class="glyphicon glyphicon-remove form-control-feedback twivalierr" aria-hidden="true"></span>');
                    controlGroup.find(".twivalierr").after('<div class="popover fade ' + popover + ' in" role="tooltip"  style="top: ' + -height + 'px; left: ' + width + 'px; display: block;"><div class="arrow" style="top: ' + arrow + '%;"></div><div class="popover-content" style="width:' + contentWidth + 'px">' + errorMsg + '</div></div>');
                }
                controlGroup.parent().find(".popover").click(function () {
                    controlGroup.parent().find(".popover").remove();
                });
            }
            else if (fstyle == 1) {

            }
            else if (fstyle == 2) {
                $thisControl = controlGroup.find('.form-control');
                var pt = parseInt($thisControl.css('padding-top'));
                var pl = parseInt($thisControl.css('padding-left'));
                var pr = parseInt($thisControl.css('padding-right'));
                height = $thisControl.height();
                width = 0;
                var left = $thisControl.width() + height + pl + pr;
                var top = -pt * 0.5;
                var msgtop = -(height - pt * 2) / 2;
                if (popover == "top") {
                    left = 0;
                    msgtop = -(height + pt * 3.5);
                } else {


                }
                if (errorMsg == "") {
                    $thisControl.after('<span class="glyphicon glyphicon-ok form-control-feedback twivalierr" style="height: ' + height + 'px;width: ' + height + 'px; top: ' + top + 'px;right: ' + height + 'px;"  aria-hidden="true"></span>');

                }
                else {
                    $thisControl.after('<span class="glyphicon glyphicon-remove form-control-feedback twivalierr" style="height: ' + height + 'px;width: ' + height + 'px; top:  ' + top + 'px;right: ' + height + 'px;"  aria-hidden="true"></span>');

                    $thisControl.after('<div class="popover fade ' + popover + ' in" role="tooltip"  style="top: ' + msgtop + 'px; left: ' + left + 'px;display: block;"><div class="arrow" style="top: ' + arrow + '%;"></div><div class="popover-content" style="width:' + contentWidth + 'px;padding:' + height * 0.25 + 'px;">' + errorMsg + '</div></div>');

                }
                $(form).find(".popover").click(function () {
                    $(form).find(".popover").remove();
                });
            }

        };//end !form
        return !error;
    };

    /**
     * 表单验证方法
     */
    var validationForm = function (obj) {

        //1.丢失焦点事件
        $(obj).find('input, textarea').each(function () {
            var el = $(this);
            el.on('blur', function () { // 失去焦点时
                valid = (el.attr('twiforvalidate') == undefined) ? null : el.attr('twiforvalidate').split(' ');
                if (valid) {

                    validateField(this, valid);
                }
            });
            el.on('focus', function () { // 获得焦点
                valid = (el.attr('twiforvalidate') == undefined) ? null : el.attr('twiforvalidate').split(' ');
                if (valid) {
                    el.parent().find(".popover").remove();
                }
            });
        });

        //2.如是文件选择则要处理onchange事件
        $(obj).find("input[type='file']").each(function () {
            var el = $(this);
            el.on('change', function () { //
                valid = (el.attr('twiforvalidate') == undefined) ? null : el.attr('twiforvalidate').split(' ');
                if (valid) {
                    validateField(this, valid);
                }
            });
        });

        //3.设置必填的标志*号
        if (globalOptions.reqmark == true) {
            if (fform_style == 0) {
                $(obj).find(".form-group>label").each(function () {
                    var el = $(this);
                    var controlGroup = el.parents('.form-group');
                    controlGroup.removeClass('has-error has-success');
                    controlGroup.find("#autoreqmark").remove();
                    //el.after('<span class="glyphicon glyphicon-remove form-control-feedback" aria-hidden="true"></span>');
                });
            }
            else if (fform_style == 1) {

            }
            else if (fform_style == 2) {

                $(obj).find('input, textarea').each(function () {
                    var el = $(this);
                    var controlGroup = el.parents('.form-group');
                    controlGroup.removeClass('has-error has-success');
                    controlGroup.find("#valierr").remove();
                    valid = (el.attr('twiforvalidate') == undefined) ? null : el.attr('twiforvalidate').split(' ');
                    if (valid) {
                        if ($.inArray('required', valid) >= 0) {
                            //el.parent().after('<span class="glyphicon glyphicon-remove form-control-feedback" aria-hidden="true"></span>');
                        }
                    };
                });
            };
        };//end showrequired

    };
}(window.jQuery);